/**
 * 
 */
package cz.cuni.mff.abacs.burglar.logics;

import cz.cuni.mff.abacs.burglar.logics.objects.BaseInterface;
import cz.cuni.mff.abacs.burglar.logics.objects.BaseObject;
import cz.cuni.mff.abacs.burglar.logics.objects.agents.Agent;
import cz.cuni.mff.abacs.burglar.logics.objects.agents.Guard;
import cz.cuni.mff.abacs.burglar.logics.objects.positions.Active;
import cz.cuni.mff.abacs.burglar.logics.objects.positions.Lockable;
import cz.cuni.mff.abacs.burglar.logics.objects.positions.Position;
import cz.cuni.mff.abacs.burglar.logics.objects.positions.Vender;


/**
 * Object representing the executable actions of the player.
 * 
 * @author abacs
 *
 */
public class Player {
	// constants:
	
	/** Penalty for pressing the show intent button. */
	public static final int PENALTY_INTENT_SHOW = 100;
	/** Penalty for pressing the increase intent distance button. */
	public static final int PENALTY_INTENT_INCREASED = 20;
	/** Penalty for activating an object. */
	public static final int PENALTY_OBJECT_ACTIVATED = 500;
	/** Penalty for closing an object. */
	public static final int PENALTY_OBJECT_CLOSED = 100;
	/** Penalty for locking an object. */
	public static final int PENALTY_OBJECT_LOCKED = 100;
	/** Penalty for stunning an agent. */
	public static final int PENALTY_AGENT_STUNNED = 1000;
	/** Penalty for activating an vending machine. */
	public static final int PENALTY_VENDER_ACTIVATED = 50;
	
	
	// -------------------------------------------------------------------------
	// properties:
	
	
	/** Holder of the game level in which the player is playing. */
	protected GameMap _gameMap;
	
	
	/** The penalties collected so far. */
	protected int _penalty = 0;
	
	
	/** The currently selected agent to visualize it's intent. */
	protected Agent _selectedAgent = null;
	
	
	/** If negative, draws all burglar intents. */
	protected int _intentDistance = PENALTY_INTENT_SHOW / PENALTY_INTENT_INCREASED;
	
	
	// -------------------------------------------------------------------------
	// constructors:
	
	
	/**
	 * 
	 * @param map
	 */
	public Player(GameMap map) {
		this._gameMap = map;
	}
	
	
	// -------------------------------------------------------------------------
	// penalty:
	
	
	/**
	 * Returns the current amount of penalty points.
	 */
	public int getPenalty() {
		return this._penalty;
	}
	
	
	/**
	 * Increases the current amount of penalty points by the given amount.
	 */
	protected void increasePenalty(int penalty) {
		this._penalty += penalty;
	}
	
	
	// -------------------------------------------------------------------------
	// agent selection:
	
	
	/** 
	 * If agent is already selected, selection sets to null.
	 * 
	 * @param agent the clicked agent
	 * @return the clicked agent or null
	 */
	public Agent selectAgent(Agent agent) {
		if(this._selectedAgent == agent){
			this._selectedAgent = null;
			
			// debug print:
			System.out.println("Hide intent of agent " + agent.getId());
		}else{
			this._selectedAgent = agent;
			
			// add point penalty to the player:
			this._penalty += PENALTY_INTENT_SHOW;
			
			// debug print:
			System.out.println("Show intent of agent " + agent.getId());
		}
		
		return this._selectedAgent;
	}
	
	
	/**
	 * Returns the selected agent that we follow or null.
	 */
	public Agent getSelectedAgent() {
		return this._selectedAgent;
	}
	
	
	// -------------------------------------------------------------------------
	// intent line:
	
	
	/**
	 * Increases the length of visualized future instructions.
	 */
	public void increaseIntentDistance() {
		this._intentDistance++;
		
		// debug print:
		System.out.println("Intent length increased");
		
		// add point penalty to the player:
		this._penalty += PENALTY_INTENT_INCREASED;
	}
	
	
	/**
	 * Decreases the length of visualized future instructions.
	 */
	public void decreaseIntentDistance() {
		if(this._intentDistance > 0)
			this._intentDistance--;
	}
	
	
	/**
	 * Returns the length of visualized future instructions.
	 */
	public int getIntentDistance() {
		return this._intentDistance;
	}
	
	
	// -------------------------------------------------------------------------
	
	
	/**
	 * Opens a closed, or closes an opened door or container.
	 * 
	 * @param position the selected object
	 * @return success indicator
	 */
	public boolean openClosePosition(Lockable position) {
		boolean result;
		if(position.isClosed()){
			result = position.open();
			
			// debug print:
			if(result)
				System.out.println("Object opened " + ((BaseObject)position).getId());
		}else{
			result = position.close();
			
			// debug print:
			if(result)
				System.out.println("Object closed " + ((BaseObject)position).getId());
		}
		
		// add point penalty to the player:
		this._penalty += PENALTY_OBJECT_CLOSED;
		
		return result;
	}
	
	
	/**
	 * Unlocks a locked, or locks an closed door or container.
	 * 
	 * @param position the selected object
	 * @return success indicator
	 */
	public boolean lockUnlockPosition(Lockable position) {
		if(position.isClosed() == false)
			return false;
		if(position.isLocked()){
			position.unlock(position.getKeyId());
			
			// debug print:
			System.out.println("Object unlocked " + ((BaseObject)position).getId());
		}else{
			position.lock(position.getKeyId());
			
			// debug print:
			System.out.println("Object locked " + ((BaseObject)position).getId());
		}
		
		// add point penalty to the player:
		this._penalty += PENALTY_OBJECT_LOCKED;
		
		return true;
	}
	
	
	/**
	 * Activates or deactivates the selected active position and adds penalty for it.
	 * 
	 * @param position
	 * @return success indicator
	 */
	public boolean activateDeactivatePosition(Active position) {
		if(position.isActive()){
			position.deactivate();
			
			// debug print:
			System.out.println("Object deactivated " + ((BaseObject)position).getId());
		}else{
			position.activate();
			
			// debug print:
			System.out.println("Object activated " + ((BaseObject)position).getId());
		}
		
		// add point penalty to the player:
		this._penalty += PENALTY_OBJECT_ACTIVATED;
		
		return true;
	}
	
	
	/**
	 * Activates the selected vender and adds penalty for it.
	 * 
	 * @param position
	 * @return success indicator
	 */
	public boolean activateVender(Vender position) {
		if(position.hasDropped())
			return false;
		
		position.activate();
		
		// debug print:
		System.out.println("Vender activated " + ((BaseObject)position).getId());
		
		// add point penalty to the player:
		this._penalty += PENALTY_VENDER_ACTIVATED;
			
		return true;
	}
	
	
	/**
	 * Dazes the selected guard and adds penalty for it.
	 * The agent gets stunned.
	 * 
	 * @param agent
	 * @return success indicator
	 */
	public boolean dazeAgent(Guard agent) {
		if(agent.isTypeOf(BaseInterface.Type.GUARD)){
			agent.daze();
			
			// debug print:
			System.out.println("Agent dazed " + agent.getId());
			
			// add point penalty to the player:
			this._penalty += PENALTY_AGENT_STUNNED;
			
			return true;
		}
		
		return false;
	}
	
	
	// -------------------------------------------------------------------------
	
	
	/**
	 * Executes a directional move instruction.
	 * 
	 * @param agent acting agent
	 * @param direction movement direction
	 * @return success of the operation
	 */
	public boolean moveAgent(Agent agent, GameMap.Direction direction) {
		return ((ExecutingMap)(_gameMap)).move(agent, direction);
	}
	
	
	/**
	 * Executes a directional move instruction.
	 * 
	 * @param agentId acting agent
	 * @param direction movement direction
	 * @return success of the operation
	 */
	public boolean moveAgent(int agentId, GameMap.Direction direction) {
		return ((ExecutingMap)(_gameMap)).move(agentId, direction);
	}
	
	
		
	/**
	 * Executes a directional move instruction.
	 * 
	 * @param direction movement direction
	 * @return success of the operation
	 */
	public boolean moveBurglar(GameMap.Direction direction) {
		if(this._gameMap == null)
			return false;
		
		return ((ExecutingMap)(this._gameMap)).move(this._gameMap.getBurglar(), direction);
	}
	
			
}
